/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package scripting;

import button.LatchWhenPressedButton;
import com.sun.squawk.util.StringTokenizer;
import edu.wpi.first.wpilibj.Relay;
import edu.wpi.first.wpilibj.SpeedController;
import edu.wpi.first.wpilibj.templates.DriverStation708;
import edu.wpi.first.wpilibj.templates.RobotParts;
import java.util.Hashtable;

/**
 * The CommandFactory class is a wrapper for classes defining script command
 * objects, all implementing the Command interface. A instance of type CommandFactory
 * is kept in the ScriptEngine object. The CommandFactory class should have static instances
 * of all robot parts available to Command objects that require them during
 * execution.
 * @author Connor Willison
 */
public class CommandFactory {
    
    private static RobotParts robot;
    public static Hashtable scriptVariables;; //binds script variable strings to their values
    private Hashtable commandNames; //binds command strings to the correct Command class
    
    private static final String variablePrefix = "&";
    
    //accepts instance of RobotParts (from main class)
    public CommandFactory(RobotParts robot){
        this.robot = robot;
        scriptVariables = new Hashtable();
	commandNames = new Hashtable();

	
	//associate command classes with strings
	commandNames.put("motor", Motor.class);
        commandNames.put("motorstop",MotorStop.class);
        commandNames.put("waitswitchpress",WaitSwitchPress.class);
        commandNames.put("motorpotset",MotorPotSet.class);
    }
    
    public static class MotorPotSet extends Command{
        private int index = 0;
        
        /**
         * Syntax:
         * MotorPotSet id= <#>
         * Wait //should be followed by a wait
         * @param parameters 
         */
        public void passParameters(StringTokenizer parameters){
            String param;
            
           while(parameters.hasMoreTokens()){
               param = parameters.nextToken();
               
               if(param.equalsIgnoreCase("id=")){
                   if(parameters.hasMoreTokens()){
                       param = parameters.nextToken();
                       if(CommandFactory.isVariable(param)){
                           index = (int)CommandFactory.getVariableValue(param);
                       }else{
                           index = Integer.parseInt(param);
                       }
                   }
               }
           }
        }
        
        public boolean execute(){
            robot.setMotor(index,(robot.getPotentiometer().getAverageValue() - 500)/500.0);
            return DriverStation708.lswitch.getButtonState();
        }
        
        public String toString(){
            return "Command: MotorPotSet";
        }
        
    }
    
    public static class WaitSwitchPress extends Command{
        
        public void passParameters(StringTokenizer parameters){}
        
        //command finishes only the FIRST TIME the button is read after a press.
        public boolean execute(){
            return DriverStation708.lswitch.getButtonState();
        }
        
        public String toString(){
            return "Command: WaitSwitchPress";
        }
    }
    
    public static class MotorStop extends Command{
        
        public void passParameters(StringTokenizer parameters){}
        public boolean execute(){
            robot.stopMotors();
            
            return true;
        }
        
        public String toString(){return "Command: MotorStop";}
    }
    
    /*
     * Motor command: will set a specified motor to
     * a specified speed.
     * 
     * Syntax:
     * 
     * Motor id= <motor id # (see array)> speed= <0.0 - 1.0>
     */
    public static class Motor extends Command{
	private int index = 0;
	private double speed = 0;
	
	public void passParameters(StringTokenizer parameters){
	    String paramName,paramValue;
	    while(parameters.hasMoreTokens()){
		paramName = parameters.nextToken();
		
		if(paramName.equalsIgnoreCase("id=")){
		    paramValue = parameters.nextToken();
		    
		    if(isVariable(paramValue)){
			index = (int)getVariableValue(paramValue);
		    }else{
			index = (int)Double.parseDouble(paramValue);
		    }
		}else if(paramName.equalsIgnoreCase("speed=")){
		    paramValue = parameters.nextToken();
		    
		    if(isVariable(paramValue)){
			speed = getVariableValue(paramValue);
		    }else{
			speed = Double.parseDouble(paramValue);
		    }
		}
		
	    }
	}
	
	public boolean execute(){
	    robot.setMotor(this.index,this.speed);
	    return true;
	}
	
	public String toString(){
	    return "Command: Motor id: " + index + " speed: " + speed;
	}
    }
    
    
    
    /**
     * Returns whether or not the specified command
     * has been defined.
     * @param commandName
     * @return 
     */
    public boolean isDefined(String commandName){
        return commandNames.containsKey(commandName);
    }
    
    public Command getCommand(String commandName) throws Exception{
        Class c = (Class)commandNames.get(commandName);
        return (Command) c.newInstance();
    }
    
    /**
     * This method translates a defined variable in a script into a
     * double value. An IllegalArgumentException is thrown if an
     * undefined variable name is passed to it. This allows command
     * classes to implement error recovery code.
     * @param variable
     * @return
     * @throws IllegalArgumentException 
     */
    public static double getVariableValue(String variable)throws IllegalArgumentException{
	
	//parse off the prefix if it is there
	if(variable.startsWith(variablePrefix)){
	    variable = variable.substring(1,variable.length());
	}
	
	//look up the varible in the hashtable
	if(scriptVariables.containsKey(variable)){
	    Double d = (Double)scriptVariables.get(variable);
	    return d.doubleValue();
	}else{
	    throw new IllegalArgumentException("Undefined script variable found: " + "\"" + variable + "\"");
	}
    }
    
    /**
     * This method can be called to determine whether or not a given parameter
     * is a script variable. Note: This does not return whether or not the variable has
     * been defined. It only determines if the string has syntax indicative of
     * a variable, i.e. &foo
     * @param parameter
     * @return 
     */
    public static boolean isVariable(String parameter){
	return parameter.startsWith(variablePrefix);
    }
}
